/*
 Copyright (c) 2010 Romuald CONTY <conty.romuald@free.fr>, François DELBOUVE

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 */


import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.*;

import java.util.Random;
import java.util.Vector;
import java.util.Timer;
import java.util.TimerTask;

import javax.microedition.m3g.*;


public class GZero3D extends MIDlet implements CommandListener
{
    public GZero3D()
    {
		// Set up the user interface.
        myDisplay = Display.getDisplay(this);
        myCanvas = new CallbackCanvas(this);
        myCanvas.setCommandListener(this);
        myCanvas.addCommand(exitCommand);
    }

    public void startApp() throws MIDletStateChangeException
    {
    	try
        {
            Image2D myBGImage=null;
            //Image2D myFloorImage=null;
          
            myBGImage = (Image2D)Loader.load("/fond.png")[0];
            
            /*try
            {
                myFloorImage = (Image2D)Loader.load("/matrix.png")[0];
            }catch(Exception e)
            {
                 // couldn't load the PNG file
                myFloorImage=null;
                e.printStackTrace();
            }    */
            myWorld = new World();
            if(myBGImage!=null)
            {
                Background myBackground;
                myBackground = new Background();
                myBackground.setImage(myBGImage);

                myWorld.setBackground(myBackground) ;
            }

            // In this world, we have a root group which will contain everything else
			// and which is tilted 15 degrees towards the camera.
			Group rootGroup2 = new Group();
			myWorld.addChild(rootGroup2);
			//rootGroup2.setOrientation(0.0f,1.0f,0.0f,0.0f);

			// Under this, we have a second group which will be the one we rotate
			// to get an all-round view of the game grid.
			rootGroup = new Group();
			starshipGroup = new Group();
			TargetsGroup = new Group();
			rootGroup2.addChild(rootGroup);
            rootGroup2.addChild(starshipGroup);
            rootGroup2.addChild(TargetsGroup);
			// We now create a parallel camera - parallel projection is faster than
			// perspective, and since we are rendering 512 separate objects that's a
			// saving worth having.
			myCamera = new Camera();
			starshipGroup.addChild(myCamera);
			myWorld.setActiveCamera(myCamera);
            //myCamera.setParallel(4.0f, 1.0f, -4.0f, 10.0f);

            // set the perspective
            myCamera.setPerspective(90.0f, 1.0f, 0.1f, 1000.0f);
            myCamera.setOrientation(45.0f,-1.0f,0.0f,0.0f);
            myCamera.setTranslation(0.0f,25.0f,25.0f);

	    myStarShip = CreateStarShip();
            SS_pos[0] = 0.0f;
            SS_pos[1] = 1.0f;
            SS_pos[2] = 0.0f;
            myStarShip.setUserID(1);
            starshipGroup.addChild(myStarShip);

            /*if(myFloorImage!=null)
            {
                Appearance aApp;
                aApp = new Appearance();
                Sprite3D myFloorS3D = new Sprite3D(true,myFloorImage, aApp);
                myFloorS3D.setScale(10.0f,10.0f,10.0f);

                //myFloorS3D.setOrientation(90.0f,-1.0f,1.0f,0.0f);
                rootGroup.addChild(myFloorS3D);
            }     */

            /* *********************** */
            Random rand = new Random();
            for(int i=0;i<25;i++)
            {

                Mesh OneRock;
                OneRock = CreateRock();
                int randx = rand.nextInt()%10;
                int randy = rand.nextInt()%10;
                int tx = (randx)*4;
                int tz = (randy)*4;
                OneRock.setTranslation(tx+2,2,tz+2);
                OneRock.setUserID(8);
                TargetsGroup.addChild(OneRock);
            }
            for(int i=0;i<20;i++)
            {
                for(int j=0;j<20;j++)
                {
                    if ((i+j)%2==0)
                    {

                        Mesh OneFE;
                        OneFE = CreateFieldElement();
                        int tx = (i*4)-40;
                        int tz = (j*4)-40;
                        OneFE.setTranslation(tx,0,tz);
                        OneFE.setUserID(4);
                        rootGroup.addChild(OneFE);
                    }

                }
            }

			// Attach to display
	        myDisplay.setCurrent(myCanvas);

			// Force a repaint so that we get the update loop started.
            myCanvas.repaint();
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }

    public Mesh CreateStarShip()
    {
        Mesh MeshResult;
        MeshResult = null;

    	try
        {
            int[][] aaStripLengths = {{3}, {3}, {3}, {3}, {4}};
            short[] aPos =
			{
				// Front L
				 5,  0,  0,	// C
   				 0,  0,  -10,	// A
				 0,  5,  0,	// B
  				// Front R
				 0,  5,  0,	// B
				 0,  0,  -10,	// A
				 -5,  0,  0,	// D
				// Back L
				 5,  0,  0,	// C
				 0,  5,  0,	// B
				 0,  0,  5,	// E
				// Back R
				 0,  0,  5,	// E
				 0,  5,  0,	// B
				 -5,  0,  0,	// D
				// Bottom
				 0,  0,  -10,	// A
                 5,  0,  0,	// C
                -5,  0,  0,	// D
                 0,  0,  5,	// E
			};

			byte[] aCol =
			{
				// Front L
				 0,  0, -1,	// C
				-1, -1,  0,	// A
				-1,  0,  0,	// B
  				// Front R
				-1,  0,  0,	// B
				-1, -1,  0,	// A
				 0,  0, -1,	// D
				// Back L
				 0,  0, -1,	// C
				-1,  0,  0,	// B
				 0, -1,  0,	// E
				// Back R
				 0, -1,  0,	// E
  				-1,  0,  0,	// B
				 0,  0, -1,	// D
				// Bottom
				-127, -127,  0,	// A
				 0,  0, -127,	// C
				 0,  0, -127,	// D
				 0, -127,  0,	// E
			};
			// Calculate the number of submeshes and vertices directly from the sizes
			// of the arrays. This prevents us getting a mismatch if we decide to change
			// the cells to a different shape later.
			int cSubmeshes = aaStripLengths.length;
			int cVertices = aPos.length/3;

			// We will share a default appearance between all the faces on the cube. Each
			// face is a separate "submesh" - it can have a separate appearance if we wish.
			Appearance app = new Appearance();

			// We need to specify an appearance and the submesh data for each face.
			Appearance[] appa = new Appearance[cSubmeshes];
			IndexBuffer[] iba = new IndexBuffer[cSubmeshes];

			int startIndex=0;
			for(int i=0;i<cSubmeshes;i++)
			{
				// We use the same apppearance for each.
				appa[i]=app;

				// And we create a new triangle strip array for each submesh.
				// The start index for each one just follows on from previous submeshes.
				iba[i] = new TriangleStripArray(startIndex, aaStripLengths[i]);
				for(int j=0; j<aaStripLengths[i].length;j++)
					startIndex+=aaStripLengths[i][j];
			}

			// Now we create a new vertex buffer that contains all the above information
			VertexBuffer vertexBuffer = new VertexBuffer();
			vertexBuffer.setDefaultColor(0xFFFFFFFF); // white

			{
				// Copy the vertex positions into a VertexArray object
				VertexArray vaPos = new VertexArray(cVertices, 3, 2);
				vaPos.set(0, cVertices, aPos);
				vertexBuffer.setPositions(vaPos, 0.7f, null);
			}

			{
				// Copy the vertex colors into a VertexArray object
				VertexArray vaCols = new VertexArray(cVertices, 3, 1);
				vaCols.set(0, cVertices, aCol);
				vertexBuffer.setColors(vaCols);
			}
			MeshResult = new Mesh(vertexBuffer, iba, appa);
        }
        catch(Exception e)
        {
            MeshResult = null;
            e.printStackTrace();
        }
        return MeshResult;
    }

    public Mesh CreateRock()
    {
        Mesh MeshResult;
        MeshResult = null;

    	try
        {
			int[][] aaStripLengths = {{4}, {4}, {4}, {4}, {4}, {4}};

			// These are the vertex positions
			short[] aPos =
			{
				// Front
				-2, -2,  2,	// B
				 2, -2,  2,	// C
				-2,  2,  2,	// A
				 2,  2,  2,	// D
				// Bottom
				-2, -2, -2,	// F
				 2, -2, -2,	// G
				-2, -2,  2,	// B
				 2, -2,  2,	// C
				// Top
				-2,  2,  2,	// A
				 2,  2,  2,	// D
				-2,  2, -2,	// E
				 2,  2, -2,	// H
				// Right
				 2,  2,  2,	// D
				 2, -2,  2,	// C
				 2,  2, -2,	// H
				 2, -2, -2,	// G
				// Left
				-2, -2,  2,	// B
				-2,  2,  2,	// A
				-2, -2, -2,	// F
				-2,  2, -2,	// E
				// Back
				 2, -2, -2,	// G
				-2, -2, -2,	// F
				 2,  2, -2,	// H
				-2,  2, -2	// E
			};

			// These are the colors for the vertices
			byte[] aCol =
			{
				// Front
				-1, 0, 0,
				-1, 0, 0,
				-1, 0, 0,
				 1, 1, 1,
				// Bottom
				 0, -1, 0,
				 0, -1, 0,
				 0, -1, 0,
				 0, -1, 0,
				// Top
				 0,  0, -1,
				 0,  0, -1,
				 0,  0, -1,
				 0,  0, -1,
				// Right
				-1, -1,  0,
				-1, -1,  0,
				-1, -1,  0,
				-1, -1,  0,
				// Left
				-1,  0, -1,
				-1,  0, -1,
				-1,  0, -1,
				-1,  0, -1,
				// Back
				 0, -1, -1,
				 0, -1, -1,
				 0, -1, -1,
				 0, -1, -1,
			};

			// Calculate the number of submeshes and vertices directly from the sizes
			// of the arrays. This prevents us getting a mismatch if we decide to change
			// the cells to a different shape later.
			int cSubmeshes = aaStripLengths.length;
			int cVertices = aPos.length/3;

			// We will share a default appearance between all the faces on the cube. Each
			// face is a separate "submesh" - it can have a separate appearance if we wish.
			Appearance app = new Appearance();

			// We need to specify an appearance and the submesh data for each face.
			Appearance[] appa = new Appearance[cSubmeshes];
			IndexBuffer[] iba = new IndexBuffer[cSubmeshes];

			int startIndex=0;
			for(int i=0;i<cSubmeshes;i++)
			{
				// We use the same apppearance for each.
				appa[i]=app;

				// And we create a new triangle strip array for each submesh.
				// The start index for each one just follows on from previous submeshes.
				iba[i] = new TriangleStripArray(startIndex, aaStripLengths[i]);
				for(int j=0; j<aaStripLengths[i].length;j++)
					startIndex+=aaStripLengths[i][j];
			}

			// Now we create a new vertex buffer that contains all the above information
			VertexBuffer vertexBuffer = new VertexBuffer();
			vertexBuffer.setDefaultColor(0xFFFFFFFF); // white

			{
				// Copy the vertex positions into a VertexArray object
				VertexArray vaPos = new VertexArray(cVertices, 3, 2);
				vaPos.set(0, cVertices, aPos);
				vertexBuffer.setPositions(vaPos, 1.0f, null);
			}

			{
				// Copy the vertex colors into a VertexArray object
				VertexArray vaCols = new VertexArray(cVertices, 3, 1);
				vaCols.set(0, cVertices, aCol);
				vertexBuffer.setColors(vaCols);
			}
			MeshResult = new Mesh(vertexBuffer, iba, appa);
        }
        catch(Exception e)
        {
            MeshResult = null;
            e.printStackTrace();
        }
        return MeshResult;
    }
     ///////
    public Mesh CreateFieldElement()
    {
        Mesh MeshResult;
        MeshResult = null;

    	try
        {
			int[][] aaStripLengths = {{4}};

			// These are the vertex positions
			short[] aPos =
			{
				// Top
				-4,  0,  4,	// A
				 4,  0,  4,	// D
				-4,  0, -4,	// E
				 4,  0, -4,	// H
			};

			// These are the colors for the vertices
			byte[] aCol =
			{
				// Top
				 -1,  -1,  -1,
				 -1,  -1,  -1,
				 -1,  -1,  -1,
				 0,  0,  0,
			};

			// Calculate the number of submeshes and vertices directly from the sizes
			// of the arrays. This prevents us getting a mismatch if we decide to change
			// the cells to a different shape later.
			int cSubmeshes = aaStripLengths.length;
			int cVertices = aPos.length/3;

			// We will share a default appearance between all the faces on the cube. Each
			// face is a separate "submesh" - it can have a separate appearance if we wish.
			Appearance app = new Appearance();

			// We need to specify an appearance and the submesh data for each face.
			Appearance[] appa = new Appearance[cSubmeshes];
			IndexBuffer[] iba = new IndexBuffer[cSubmeshes];

			int startIndex=0;
			for(int i=0;i<cSubmeshes;i++)
			{
				// We use the same apppearance for each.
				appa[i]=app;

				// And we create a new triangle strip array for each submesh.
				// The start index for each one just follows on from previous submeshes.
				iba[i] = new TriangleStripArray(startIndex, aaStripLengths[i]);
				for(int j=0; j<aaStripLengths[i].length;j++)
					startIndex+=aaStripLengths[i][j];
			}

			// Now we create a new vertex buffer that contains all the above information
			VertexBuffer vertexBuffer = new VertexBuffer();
			vertexBuffer.setDefaultColor(0xFFFFFFFF); // white

			{
				// Copy the vertex positions into a VertexArray object
				VertexArray vaPos = new VertexArray(cVertices, 3, 2);
				vaPos.set(0, cVertices, aPos);
		      	vertexBuffer.setPositions(vaPos,0.40f, null);
			}

			{
				// Copy the vertex colors into a VertexArray object
				VertexArray vaCols = new VertexArray(cVertices, 3, 1);
				vaCols.set(0, cVertices, aCol);
				vertexBuffer.setColors(vaCols);
			}
			MeshResult = new Mesh(vertexBuffer, iba, appa);
        }
        catch(Exception e)
        {
            MeshResult = null;
            e.printStackTrace();
        }
        return MeshResult;
    }

    ////////
     ///////
    private class Shoot
    {
        private Mesh myMesh;
        private float Pos[] = new float[3];
        private float Dir[] = new float[3];
        private Group TargetsGroup = null;
        private Group ShootsGroup = null;
        private float mySpeed = 0.0f;
        private float distanceToMesh = 0.0f;
        RayIntersection ri;
        private float targetangle = 0.0f;
        private Mesh targetMesh = null;
        private int MAX_MOVE = 100;
        private int move_counter = 0;

        public Shoot(float initX,float initY,float initZ,float dirX,float dirY, float dirZ,Group Shoots,Group Targets, float speed)
        {
        	myMesh = CreateShoot();
        	mySpeed = speed;
        	TargetsGroup = Targets;
            ShootsGroup = Shoots;
        	ri = new RayIntersection();
        	myMesh.setTranslation(initX,initY,initZ);
            Pos[0] = initX;
        	Pos[1] = initY;
        	Pos[2] = initZ;
        	Dir[0] = -dirX;
        	Dir[1] = -dirY;
        	Dir[2] = -dirZ;
            if(TargetsGroup.pick(-1, Pos[0], Pos[1], Pos[2], Dir[0], Dir[1], Dir[2], ri) == true)
        	{
        		if (ri.getDistance() !=0)
        		{
	        		distanceToMesh = ri.getDistance()*mySpeed;
                    targetMesh = (Mesh)ri.getIntersected();
                    //System.out.println("Touche : "+ Pos[0] + " " + Pos[1] + " " + Pos[2] + " " + Dir[0] + " " + Dir[1] + " " + Dir[2] + " " + ri.getDistance() + " : " + targetMesh.toString());
                }
        	}
        }

        public Mesh GetMesh()
        {
        	return myMesh;
        }

        public boolean Move()
        {
        	boolean Alive = true;
        	targetangle+=10.0f;
            myMesh.setOrientation(targetangle, 1.0f,0f,0f);
       		if (targetMesh != null)
       		{
                if(distanceToMesh > mySpeed)
        	    {
                    //System.out.println("distance : "+distanceToMesh);
                    targetMesh.setOrientation(targetangle, 1.0f,0f,0f);
                }
                else
                {
                    if(TargetsGroup.pick(-1, Pos[0], Pos[1], Pos[2], Dir[0], Dir[1], Dir[2], ri) == true)
                	{
                		//ri.getDistance();
                		Mesh tmp = (Mesh)ri.getIntersected();
                		if(targetMesh != tmp)
                		{
        	        		distanceToMesh = ri.getDistance()*mySpeed;
                           	targetMesh = tmp;
                            //System.out.println("Changement de cible : "+ Pos[0] + " " + Pos[1] + " " + Pos[2] + " " + Dir[0] + " " + Dir[1] + " " + Dir[2] + " " + ri.getDistance());
                        }
                        else
                        {
   	                    TargetsGroup.removeChild(targetMesh);
                            ShootsGroup.removeChild(myMesh);
                            Alive = false;
                            targetMesh = null;
                    	}
                    }
                  	else
                   	{
                        //System.out.println("Cible annulée...");
                        targetMesh = null;
                    }
                }
                distanceToMesh -= mySpeed;
            }
            Pos[0] += Dir[0];
        	Pos[1] += Dir[1];
        	Pos[2] += Dir[2];
            myMesh.setTranslation(Pos[0],Pos[1],Pos[2]);
            move_counter++;
            if(move_counter>MAX_MOVE)
            {
                ShootsGroup.removeChild(myMesh);
                Alive = false;
            }
            return Alive;
        }

        private Mesh CreateShoot()
        {
            Mesh MeshResult;
            MeshResult = null;

        	try
            {
    			int[][] aaStripLengths = {{4},{4},{4},{4},{4},{4}};

    			// These are the vertex positions
    			short[] aPos =
			{
				// Front
				-1, -1,  1,	// B
				 1, -1,  1,	// C
				-1,  1,  1,	// A
				 1,  1,  1,	// D
				// Bottom
				-1, -1, -1,	// F
				 1, -1, -1,	// G
				-1, -1,  1,	// B
				 1, -1,  1,	// C
				// Top
				-1,  1,  1,	// A
				 1,  1,  1,	// D
				-1,  1, -1,	// E
				 1,  1, -1,	// H
				// Right
				 1,  1,  1,	// D
				 1, -1,  1,	// C
				 1,  1, -1,	// H
				 1, -1, -1,	// G
				// Left
				-1, -1,  1,	// B
				-1,  1,  1,	// A
				-1, -1, -1,	// F
				-1,  1, -1,	// E
				// Back
				 1, -1, -1,	// G
				-1, -1, -1,	// F
				 1,  1, -1,	// H
				-1,  1, -1	// E
			};

    			// These are the colors for the vertices
    			byte[] aCol =
    			{
    				// Top
    				 -1,  -1,  0,
    				 -1,   0,  0,
    				 -1,   0,  0,
    				 -1,  -1,  0,
    				// Top
    				 -1,  -1,  0,
    				 -1,   0,  0,
    				 -1,   0,  0,
    				 -1,  -1,  0,
    				// Top
    				 -1,  -1,  0,
    				 -1,   0,  0,
    				 -1,   0,  0,
    				 -1,  -1,  0,
    				// Top
    				 -1,  -1,  0,
    				 -1,   0,  0,
    				 -1,   0,  0,
    				 -1,  -1,  0,
    				// Top
    				 -1,  -1,  0,
    				 -1,   0,  0,
    				 -1,   0,  0,
    				 -1,  -1,  0,
    				// Top
    				 -1,  -1,  0,
    				 -1,   0,  0,
    				 -1,   0,  0,
    				 -1,  -1,  0,
    			};

    			// Calculate the number of submeshes and vertices directly from the sizes
    			// of the arrays. This prevents us getting a mismatch if we decide to change
    			// the cells to a different shape later.
    			int cSubmeshes = aaStripLengths.length;
    			int cVertices = aPos.length/3;

    			// We will share a default appearance between all the faces on the cube. Each
    			// face is a separate "submesh" - it can have a separate appearance if we wish.
    			Appearance app = new Appearance();

    			// We need to specify an appearance and the submesh data for each face.
    			Appearance[] appa = new Appearance[cSubmeshes];
    			IndexBuffer[] iba = new IndexBuffer[cSubmeshes];

    			int startIndex=0;
    			for(int i=0;i<cSubmeshes;i++)
    			{
    				// We use the same apppearance for each.
    				appa[i]=app;

    				// And we create a new triangle strip array for each submesh.
    				// The start index for each one just follows on from previous submeshes.
    				iba[i] = new TriangleStripArray(startIndex, aaStripLengths[i]);
    				for(int j=0; j<aaStripLengths[i].length;j++)
    					startIndex+=aaStripLengths[i][j];
    			}

    			// Now we create a new vertex buffer that contains all the above information
    			VertexBuffer vertexBuffer = new VertexBuffer();
    			vertexBuffer.setDefaultColor(0xFFFFFFFF); // white

    			{
    				// Copy the vertex positions into a VertexArray object
    				VertexArray vaPos = new VertexArray(cVertices, 3, 2);
    				vaPos.set(0, cVertices, aPos);
    				vertexBuffer.setPositions(vaPos, 1.0f, null);
    			}

    			{
    				// Copy the vertex colors into a VertexArray object
    				VertexArray vaCols = new VertexArray(cVertices, 3, 1);
    				vaCols.set(0, cVertices, aCol);
    				vertexBuffer.setColors(vaCols);
    			}
    			MeshResult = new Mesh(vertexBuffer, iba, appa);
            }
            catch(Exception e)
            {
                MeshResult = null;
                e.printStackTrace();
            }
            return MeshResult;
        }
    }
    ////////

    public void pauseApp()
    {
        myRefreshTask.cancel();
	    myRefreshTask = null;
	    // Release resources.
	    myWorld = null;
    }

    public void destroyApp(boolean unconditional) throws MIDletStateChangeException
    {
        myRefreshTimer.cancel();
        myRefreshTimer = null;
        myRefreshTask = null;

		// Release resources.
        myWorld = null;
		myCanvas = null;
    }

    public void paint(Graphics g)
    {
		// We are not fully initialised yet; just return.
        if(myCanvas == null || myWorld == null)
            return;

        if(myRefreshTask!=null)
        {
			myRefreshTask.cancel();
			myRefreshTask=null;
		}


		Graphics3D myGraphics3D = Graphics3D.getInstance();
        myGraphics3D.bindTarget(g);
        try
        {
	        myGraphics3D.render(myWorld);
	    }
		finally
		{
	        myGraphics3D.releaseTarget();
	 	}
        if(KeyStates[1]==true) //droite
        {
        	if(SS_pitch>-30)
                SS_pitch -= 4.0f;
        }else
        {
        	if(SS_pitch<0)
        	   SS_pitch += 1.0f;
        }
        if(KeyStates[0]==true) //gauche
        {
        	if(SS_pitch<30)
                SS_pitch += 4.0f;
        }else
        {
        	if(SS_pitch>0)
        	   SS_pitch -= 1.0f;
        }

        int direction = 0;
        if (SS_pitch > 0)
        	direction = 1;
        else
        	direction = -1;

        SS_angle += 360;
        SS_angle += SS_pitch/5;
        SS_angle %= 360;


        if(KeyStates[3]==true) //haut
        {
        	if(SS_Speed<MAX_SPEED)
        	{
        		SS_Speed+= (float)((Math.abs(SS_Speed)+1)/10);
        	}
        }
        else
        {
        	if(SS_Speed>0.0f)
        	{
        		SS_Speed-= (float)((Math.abs(SS_Speed)+1)/20);
        		if(SS_Speed <0.0f)
        		  SS_Speed = 0.0f;
        	}
        }

        if(KeyStates[2]==true) //bas
        {
        	if(SS_Speed>(-MAX_SPEED))
        	{
        		SS_Speed-= (float)((Math.abs(SS_Speed)+1)/10);
            }
        }
        else
        {
        	if(SS_Speed<0.0f)
        	{
        		SS_Speed+= (float)((Math.abs(SS_Speed)+1)/20);
        		if(SS_Speed>0.0f)
        		  SS_Speed = 0.0f;
            }
        }

        SS_pos[2] -= (float)Math.cos(Math.toRadians(SS_angle))*(SS_Speed);
        SS_pos[0] -= (float)Math.sin(Math.toRadians(SS_angle))*(SS_Speed);

        //rootGroup.setOrientation(SuperCounter, 0.0f, 1.0f, 0.0f);

        myStarShip.setOrientation(SS_pitch, -0.5f*direction, 0.0f, 1.0f);
        starshipGroup.setOrientation(SS_angle,0.0f,1.0f,0.0f);
        starshipGroup.setTranslation(SS_pos[0],SS_pos[1],SS_pos[2]);

        for(int i = myShootList.size()-1;i>=0 ;i--)
        {
        	if( ((Shoot)(myShootList.elementAt(i))).Move() == false )
        	{
        		myShootList.removeElementAt(i);
        	}
        }
        /*starshipGroup.setOrientation(//SuperCounter+
        starship_angle, 0.0f, 1.0f, 0.0f);    */

        //myCamera.setTranslation(0.0f,15.0f,15.0f+SS_posz);

        g.setColor(0xFFFFFFFF);
        g.drawString("SS_angle:"+String.valueOf(SS_angle), 0, 10, Graphics.LEFT|Graphics.TOP);
        g.drawString("SS_pitch:"+String.valueOf(SS_pitch), 0, 20, Graphics.LEFT|Graphics.TOP);
        g.drawString("SS_pos[0]:"+String.valueOf(SS_pos[0]), 0, 30, Graphics.LEFT|Graphics.TOP);
        g.drawString("SS_pos[1]:"+String.valueOf(SS_pos[1]), 0, 40, Graphics.LEFT|Graphics.TOP);
        g.drawString("SS_pos[2]:"+String.valueOf(SS_pos[2]), 0, 50, Graphics.LEFT|Graphics.TOP);
        g.drawString("SS_Speed:"+String.valueOf(SS_Speed), 0, 60, Graphics.LEFT|Graphics.TOP);
        //g.drawString("Math.sin(Math.toRadians(SS_angle)):"+String.valueOf((float)Math.sin(Math.toRadians(SS_angle))), 0, 70, Graphics.LEFT|Graphics.TOP);


        SuperCounter+= 1.0f;
        SuperCounter%= 360.0f;
        System.gc();
        myRefreshTask = new RefreshTask();
        myRefreshTimer.schedule(myRefreshTask, 50);
    }

    public void commandAction(Command cmd, Displayable disp)
    {
        if (cmd == exitCommand)
        {
            try
            {
                destroyApp(false);
                notifyDestroyed();
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }
        }
    }

    public void keyReleased(int keyCode)
    {
        switch(keyCode)
		{
			case -3:
			case Canvas.KEY_NUM4:
				 KeyStates[0]=false;
				 break;

			case -4:
			case Canvas.KEY_NUM6:
			     KeyStates[1]=false;
				 break;

            case -2:
            case Canvas.KEY_NUM8:
			    KeyStates[2]=false;
				KeyStates[3]=false;
				break;

			case -1:
            case Canvas.KEY_NUM2:
			    KeyStates[3]=false;
				KeyStates[2]=false;
				break;
//			case Canvas.KEY_NUM2:
/*			case Canvas.KEY_STAR:
				loadRandomState();
				break;*/
	   }
    }

    public void keyPressed(int keyCode)
	{
		LastKeyCode = keyCode;
        Shoot myShoot = null;
        float Step = 0.0f;
        switch(keyCode)
		{

			case -3:
			case Canvas.KEY_NUM4:

				KeyStates[0]=true;
				KeyStates[1]=false;
				break;

			case -4:
			case Canvas.KEY_NUM6:

				KeyStates[1]=true;
				KeyStates[0]=false;
				break;

			case -2:
            case Canvas.KEY_NUM8:
			    KeyStates[2]=true;
				KeyStates[3]=false;
				break;

			case -1:
            case Canvas.KEY_NUM2:
			    KeyStates[3]=true;
				KeyStates[2]=false;
				break;
			case Canvas.KEY_NUM0:
                Step = 3.5f+SS_Speed;
                myShoot = new Shoot(SS_pos[0],SS_pos[1],SS_pos[2],(float)Math.sin(Math.toRadians(SS_angle))*Step,0.0f,(float)Math.cos(Math.toRadians(SS_angle))*Step, rootGroup,TargetsGroup, Step);
                myShootList.addElement(myShoot);
                rootGroup.addChild(myShoot.GetMesh());
				break;
			case Canvas.KEY_NUM9 :
                 MAX_SPEED+=1.0;
                 break;
			case Canvas.KEY_NUM1:
                Step = 1.0f;
                for(int i=0;i<36;i++)
                {
                    for(int j=0;j<1;j++)
                    {
                    	//v( - sin(?) cos(?), - sin(?) sin(?), cos(?) )
                        float dirx = ((float)Math.sin(Math.toRadians(i*10))*Step)*((float)Math.cos(Math.toRadians(j*10))*Step);
                        float diry = ((float)Math.sin(Math.toRadians(i*10))*Step)*((float)Math.sin(Math.toRadians(j*10))*Step);
                        float dirz = -((float)Math.cos(Math.toRadians(i*10))*Step);
                        myShoot = new Shoot(SS_pos[0],SS_pos[1],SS_pos[2],dirx,diry,dirz, rootGroup,TargetsGroup, Step);
                        myShootList.addElement(myShoot);
                        rootGroup.addChild(myShoot.GetMesh());
                    }
                }
				break;
			case Canvas.KEY_NUM7:
    			Random rand = new Random();
                for(int i=0;i<25;i++)
                {

                    Mesh OneRock;
                    OneRock = CreateRock();
                    int randx = rand.nextInt()%10;
                    int randy = rand.nextInt()%10;
                    int tx = (randx)*4;
                    int tz = (randy)*4;
                    OneRock.setTranslation(tx+2,2,tz+2);
                    OneRock.setUserID(8);
                    TargetsGroup.addChild(OneRock);
                }
                break;
		}
	}


    private class RefreshTask extends TimerTask
    {
        public void run()
        {
			// Get the canvas to repaint itself.
            myCanvas.repaint();
        }
    }

    private class CallbackCanvas extends Canvas
    {
        GZero3D mymidlet;

        CallbackCanvas(GZero3D midlet) { mymidlet = midlet; }

        void init() { }

        void destroy() { }

        protected void paint(Graphics g) { mymidlet.paint(g); }
        protected void keyReleased(int keyCode) { mymidlet.keyReleased(keyCode); }
        protected void keyPressed(int keyCode) { mymidlet.keyPressed(keyCode); }
    }

	// Variables.

	// Reference to the MIDlet's display
    private Display myDisplay = null;

    // Reference to the current canvas.
    private CallbackCanvas myCanvas = null;
    // Reference to the current camera.
    private Camera myCamera =  null;
	// Reference to a refresh timer, which is used to schedule screen updates
    private Timer myRefreshTimer = new Timer();

	// Reference to a TimerTask-derived object that calls back to the MIDlet
    private TimerTask myRefreshTask = null;

	// A command which is used to exit from the MIDlet.
    private Command exitCommand = new Command("Exit", Command.ITEM, 1);

	// Reference to the world which contains the 3D cells.
    private World myWorld = null;

	// The root group, which is the one we rotate slowly.
    private Group rootGroup = null;
    private Group TargetsGroup = null;
    private Group starshipGroup = null;
    private Mesh myStarShip = null;

    private int LastKeyCode = 0;

    private boolean KeyStates[] = {false,false,false,false};

    private float SS_pitch = 0.0f;
    private float SuperCounter = 0.0f;
    private float SS_angle = 0.0f;
   	private float[] SS_pos = new float[3];
   	private Vector myShootList = new Vector();
    private float MAX_SPEED = 3.0f;
   	private float SS_Speed = 0.0f;
}